package com.example.demospringbootfreemarker.util;

import freemarker.cache.ByteArrayTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import org.w3c.dom.Document;
import org.xhtmlrenderer.swing.Java2DRenderer;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

/**
 * 磅单图片生成工具类
 *
 * @author ke 2022/7/12.
 */
@Slf4j
@Component
public class FreeMakerUtils {

    /**
     * 通过远程URL地址获取模板
     * 此方法可以通过URL加载存储在远程服务器上的模板
     *
     * @param template
     * @param map
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    private String getTemplateByUrl(String template, Map<String, Object> map, String url) throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
        ByteArrayTemplateLoader byteArrayTemplateLoader = new ByteArrayTemplateLoader();
        InputStream initialStream = getInputStreamByGet(url);
        byteArrayTemplateLoader.putTemplate(template, IOUtils.toByteArray(initialStream));
        cfg.setTemplateLoader(byteArrayTemplateLoader);
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setClassicCompatible(true);
        Template temp = cfg.getTemplate(template);
        StringWriter stringWriter = new StringWriter();
        temp.process(map, stringWriter);
        stringWriter.flush();
        stringWriter.close();
        String result = stringWriter.getBuffer().toString();
        return result;
    }

    /**
     * 通过类加载器的方式获取模板
     * springboot项目在部署的时候会打包成jar，打包成jar以后在使用freemaker时会出现以下报错：
     *           cannot be resolved to absolute file path because it does not reside in the file system: jar
     * 通过以下 setClassLoaderForTemplateLoading() 方法设置成类加载器的方式，可以解决上述无法访问模板路径的问题
     * @param template
     * @param map
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    private String getTemplateByClassLoader(String template, Map<String, Object> map) throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
        cfg.setClassLoaderForTemplateLoading(getClass().getClassLoader(), "templates");
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setClassicCompatible(true);
        Template temp = cfg.getTemplate(template);
        StringWriter stringWriter = new StringWriter();
        temp.process(map, stringWriter);
        stringWriter.flush();
        stringWriter.close();
        String result = stringWriter.getBuffer().toString();
        return result;
    }

    /**
     * 通过指定classpath:templates读取指定模板
     * 如果是打成war包，或者指定服务器绝对路径的时候，可以使用此方法
     *
     * @param template
     * @param map
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    private String getTemplateByTemplatePath(String template, Map<String, Object> map) throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
        String templatePath = ResourceUtils.getFile("classpath:templates").getPath();
        cfg.setDirectoryForTemplateLoading(new File(templatePath));
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        cfg.setLogTemplateExceptions(false);
        cfg.setClassicCompatible(true);
        Template temp = cfg.getTemplate(template);
        StringWriter stringWriter = new StringWriter();
        temp.process(map, stringWriter);
        stringWriter.flush();
        stringWriter.close();
        String result = stringWriter.getBuffer().toString();
        return result;
    }

    /**
     * ftl模板生成图片接口
     *
     * @param filename    生成图片名称
     * @param templateUrl ftl模板路径
     * @param template    ftl模板名称
     * @param map         模板占位符数据
     * @throws Exception
     */
    public byte[] turnImage(String templateUrl, String template, Map<String, Object> map, String filename) throws Exception {
        // 写出到流
        String html = getTemplateByUrl(template, map, templateUrl);
        byte[] bytes = html.getBytes("UTF-8");
        ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(bin);
        Java2DRenderer renderer = new Java2DRenderer(document, 1000, 500);
        BufferedImage img = renderer.getImage();
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        ImageIO.write(img, filename, outStream);
        return outStream.toByteArray();
    }

    /**
     * ftl模板生成图片接口，并输出到浏览器
     * @param template
     * @param map
     * @param response
     * @param url
     * @throws Exception
     */
    public void turnImage(String template, Map<String,Object> map, HttpServletResponse response,String url) throws Exception {
        //方式一：指定模板文件路径加载模板
//        String html = getTemplateByTemplatePath(template, map);
        //方式二：指定类加载器加载模板
//        String html = getTemplateByClassLoader(template, map);
        //方式三：指定远程模板文件存储路径加载模板
        String html = getTemplateByUrl(template, map,url);
        byte[] bytes=html.getBytes();
        ByteArrayInputStream bin=new ByteArrayInputStream(bytes);
        DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
        DocumentBuilder builder=factory.newDocumentBuilder();
        Document document=builder.parse(bin);
        Java2DRenderer renderer = new Java2DRenderer(document,1000,800);
        BufferedImage img = renderer.getImage();
        response.setContentType("image/jpeg");
        response.setDateHeader("expries", -1);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        ImageIO.write(img, "jpg", response.getOutputStream());
    }

    /**
     * 图片盖章
     *
     * @param stampedImageUrl 图片
     * @param sealImageUrl    印章图片
     * @param filename        图片名称
     * @return
     * @throws Exception
     */
    public byte[] imageSynthesis(String stampedImageUrl, String sealImageUrl, String filename) {
        try {
            InputStream stampedImageIn = getInputStreamByGet(stampedImageUrl);
            BufferedImage stampedImage = ImageIO.read(stampedImageIn);
            Graphics g = stampedImage.getGraphics();
            InputStream sealImageIn = getInputStreamByGet(sealImageUrl);
            BufferedImage sealImage = ImageIO.read(sealImageIn);
            //加盖图片章
            int x = stampedImage.getWidth() - sealImage.getWidth() - 100;
            int y = stampedImage.getHeight() - sealImage.getHeight() - 100;
            g.drawImage(sealImage, x, y, null);
            g.dispose();
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            ImageIO.write(stampedImage, filename, outStream);
            return outStream.toByteArray();
        } catch (FileNotFoundException e) {
            log.error("文件找不到：{}，{}", e.getMessage(), e);
        } catch (IOException e) {
            log.error("文件转图片IO异常：{}，{}", e.getMessage(), e);
        } catch (Exception e) {
            log.error("图片盖章异常：{}，{}", e.getMessage(), e);
        }
        return null;
    }

    /**
     * 通过get请求得到读取器响应数据的数据流
     *
     * @param url
     * @return
     * @throws Exception
     */
    public InputStream getInputStreamByGet(String url) throws Exception {
        InputStream inputStream = null;
        HttpURLConnection conn = (HttpURLConnection) new URL(url)
                .openConnection();
        conn.setReadTimeout(5000);
        conn.setConnectTimeout(5000);
        conn.setConnectTimeout(5000);
        conn.addRequestProperty("Referer","service.sijibao.com");
        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
            inputStream = conn.getInputStream();
        }
        return inputStream;
    }


}